"""
Time's up - PPT自动计时工具
支持Office PowerPoint和WPS演示的自动计时与提醒
"""


# 基础库
import sys
import json
import time
import threading
import winsound
import urllib.request
import webbrowser
from datetime import datetime, timedelta
from pathlib import Path

# PyQt5相关类（显式导入，避免未绑定问题）
try:
    from PyQt5.QtCore import QThread, QTimer, Qt, pyqtSignal, QPoint
    from PyQt5.QtWidgets import (
        QWidget, QLabel, QVBoxLayout, QHBoxLayout, QFormLayout, QSpinBox, QComboBox, QCheckBox, QPushButton, QDialog, QGroupBox, QListWidget, QInputDialog, QMessageBox, QSystemTrayIcon, QAction, QMenu, QApplication
    )
    from PyQt5.QtGui import QIcon, QPixmap, QPainter, QBrush, QColor, QPen
except ImportError:
    print("请安装PyQt5: pip install PyQt5")
    sys.exit(1)

# win32相关库
try:
    import win32gui
    import win32con
    import win32api
    import win32process
except ImportError:
    print("请安装pywin32: pip install pywin32")
    sys.exit(1)

# psutil库
try:
    import psutil
except ImportError:
    print("请安装psutil: pip install psutil")
    sys.exit(1)

# 应用版本
VERSION = "1.4.1"


class ConfigManager:
    """配置管理器"""
    def __init__(self):
        self.config_file = Path("config.json")
        self.default_config = {
            "timer_duration": 600,  # 默认10分钟
            "timer_mode": "countdown",  # 计时模式: countdown (倒计时) or countup (正计时)
            "window_x": -1,  # -1表示使用默认位置
            "window_y": -1,
            "remember_position": True,
            "exit_mode": "exit_fullscreen",  # exit_fullscreen, close_ppt, 或 overtime_mode
            "sound_enabled": True,
            "vibration_enabled": True,
            "black_screen_enabled": True,  # 黑屏提醒
            "warning_times": [30, 60],  # 剩余30秒和60秒时提醒
            "segment_mode": False,  # 分段计时模式
            "segments": [300, 300, 300],  # 默认分段时间（秒）
            "loop_mode": False,  # 循环计时模式
            "loop_count": 3,  # 循环次数
            "current_segment": 0,  # 当前分段
            "current_loop": 0,  # 当前循环
            "overtime_black_screen_interval": 30  # 超时黑屏间隔（秒）
        }
        self.config = self.load_config()

    def load_config(self):
        """加载配置"""
        if self.config_file.exists():
            try:
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    # 合并默认配置以确保所有键都存在
                    return {**self.default_config, **config}
            except:
                pass
        return self.default_config.copy()

    def save_config(self):
        """保存配置"""
        try:
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(self.config, f, indent=2, ensure_ascii=False)
        except Exception as e:
            print(f"保存配置失败: {e}")


class PPTMonitor(QThread):
    """PPT进程监控线程"""
    
    ppt_detected = pyqtSignal(str)  # 发送PPT类型信号
    ppt_lost = pyqtSignal()
    
    def __init__(self):
        super().__init__()
        self.running = True
        self.ppt_processes = {
            'POWERPNT.EXE': 'PowerPoint',
            'wps.exe': 'WPS',
            'wpp.exe': 'WPS'
        }
        self.current_ppt = None
        
    def run(self):
        """监控线程主循环"""
        print("PPT监控线程启动...")
        
        while self.running:
            try:
                ppt_found = False
                fullscreen_found = False
                
                for proc in psutil.process_iter(['pid', 'name']):
                    try:
                        proc_name = proc.info['name']
                        if proc_name in self.ppt_processes:
                            ppt_found = True
                            
                            # 检查是否全屏
                            if self.is_fullscreen(proc.info['pid']):
                                fullscreen_found = True
                                if self.current_ppt != proc_name:
                                    self.current_ppt = proc_name
                                    ppt_type = self.ppt_processes[proc_name]
                                    print(f"✓ 检测到{ppt_type}全屏播放，开始计时")
                                    self.ppt_detected.emit(ppt_type)
                                break
                    except (psutil.NoSuchProcess, psutil.AccessDenied):
                        continue
                
                # 处理状态变化
                if not fullscreen_found and self.current_ppt:
                    if ppt_found:
                        print(f"○ PPT运行中但未全屏，停止计时")
                    else:
                        print(f"○ PPT已关闭，停止计时")
                    self.current_ppt = None
                    self.ppt_lost.emit()
                    
            except Exception as e:
                print(f"监控错误: {e}")
            
            time.sleep(2)  # 每2秒检查一次
        
        print("PPT监控线程已停止")
    
    def is_fullscreen(self, pid):
        """检查进程是否全屏"""
        try:
            def enum_windows_callback(hwnd, windows):
                if win32gui.IsWindowVisible(hwnd):
                    _, window_pid = win32process.GetWindowThreadProcessId(hwnd)
                    if window_pid == pid:
                        windows.append(hwnd)
                return True
            
            windows = []
            win32gui.EnumWindows(enum_windows_callback, windows)
            
            for hwnd in windows:
                try:
                    # 获取窗口标题
                    window_title = win32gui.GetWindowText(hwnd).lower()
                    
                    # 检查窗口标题是否包含全屏播放的关键词
                    fullscreen_keywords = [
                        '幻灯片放映',
                        'slide show',
                        'slideshow',
                        '演示播放',
                        'powerpoint slide show',
                        '全屏'
                    ]
                    
                    # 如果标题包含全屏关键词，再检查窗口大小
                    title_indicates_fullscreen = any(keyword in window_title for keyword in fullscreen_keywords)
                    
                    if title_indicates_fullscreen:
                        window_rect = win32gui.GetWindowRect(hwnd)
                        screen_width = win32api.GetSystemMetrics(win32con.SM_CXSCREEN)
                        screen_height = win32api.GetSystemMetrics(win32con.SM_CYSCREEN)
                        
                        # 检查窗口是否覆盖大部分屏幕
                        if (window_rect[2] - window_rect[0] >= screen_width * 0.8 and
                            window_rect[3] - window_rect[1] >= screen_height * 0.8):
                            print(f"✓ 检测到全屏播放: {window_title}")
                            return True
                    
                except Exception as e:
                    continue
                    
            return False
        except Exception as e:
            print(f"全屏检测错误: {e}")
            return False
    
    def stop(self):
        """停止监控"""
        self.running = False


class CountdownWindow(QWidget):
    """倒计时窗口"""
    
    def __init__(self, config_manager):
        super().__init__()
        self.config_manager = config_manager
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_countdown)
        self.remaining_time = 0
        self.total_time = 0
        self.warned_times = set()
        self.shake_timer = QTimer()
        self.shake_timer.timeout.connect(self.shake_step)
        self.shake_count = 0
        self.original_pos = None
        
        # 分段计时相关
        self.current_segment_index = 0
        self.segment_times = []
        self.is_segment_mode = False
        
        # 循环计时相关
        self.current_loop = 0
        self.total_loops = 0
        self.is_loop_mode = False
        
        # 超时模式相关
        self.is_overtime_mode = False
        self.overtime_timer = QTimer()
        self.overtime_timer.timeout.connect(self.overtime_black_screen)
        
        self.init_ui()
    
    def init_ui(self):
        """初始化UI"""
        self.setWindowTitle("Time's up - 倒计时")
        from PyQt5.QtCore import Qt
        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        
        # 创建圆角半透明背景
        self.setFixedSize(300, 120)  # 原来是200x80，放大1.5倍
        
        layout = QVBoxLayout()
        layout.setContentsMargins(15, 15, 15, 15)
        
        # 时间显示
        self.time_label = QLabel("00:00")
        self.time_label.setAlignment(Qt.AlignCenter)
        self.time_label.setStyleSheet("""
            QLabel {
                color: white;
                font-size: 36px;
                font-weight: bold;
                background: rgba(0, 0, 0, 180);
                border-radius: 15px;
                padding: 15px;
            }
        """)
        layout.addWidget(self.time_label)
        self.setLayout(layout)
        
        # 设置窗口位置
        self.position_window()
        
        # 使窗口可拖动
        self.drag_position = None

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.drag_position = event.globalPos() - self.frameGeometry().topLeft()

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.MouseButton.LeftButton and self.drag_position:
            self.move(event.globalPos() - self.drag_position)
    
    def position_window(self):
        """设置窗口位置"""
        config = self.config_manager.config
        if config['remember_position'] and config['window_x'] >= 0 and config['window_y'] >= 0:
            self.move(config['window_x'], config['window_y'])
        else:
            # 默认位置：右上角
            screen = QApplication.primaryScreen()
            if screen:
                geometry = screen.geometry()
                self.move(geometry.width() - self.width() - 50, 50)
            else:
                self.move(1200, 50)  # 备用位置
    
    def mouse_press_event(self, event):
        """鼠标按下事件"""
    # 已在mousePressEvent中实现
    
    def mouse_move_event(self, event):
        """鼠标移动事件"""
    # 已在mouseMoveEvent中实现
    
    def start_countdown(self, duration):
        """开始计时（倒计时或正计时）"""
        config = self.config_manager.config

        # 计时模式：倒计时 (countdown) 或 正计时 (countup)
        cfg_mode = config.get('timer_mode', 'countdown')
        self.is_countup = True if cfg_mode == 'countup' else False

        if self.is_countup:
            # 正计时模式：完全独立的逻辑
            self.start_countup(duration)
        else:
            # 倒计时模式：保持原有逻辑
            self.start_countdown_internal(duration)

    def start_countup(self, duration):
        """启动正计时模式"""
        # 正计时：简单累加时间，只支持提醒功能
        self.remaining_time = 0  # 正计时从0开始
        self.total_time = duration  # 仅用于显示参考
        self.is_segment_mode = False  # 正计时不支持分段
        self.is_loop_mode = False     # 正计时不支持循环
        self.current_loop = 0
        self.total_loops = 0
        self.warned_times.clear()
        
        # 为正计时模式添加关闭按钮
        self.add_close_button()
        
        # 设置窗口样式为正计时（浅绿色背景，与倒计时保持相同透明度）
        self.setStyleSheet("""
            QWidget {
                background-color: rgba(76, 175, 80, 200);
                border: 2px solid #4CAF50;
                border-radius: 10px;
            }
            QLabel {
                color: white;
                font-weight: bold;
            }
            QPushButton {
                background-color: #4CAF50;
                color: white;
                border: none;
                border-radius: 5px;
                padding: 5px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #45A049;
            }
        """)
        
        self.update_display()
        self.timer.start(1000)  # 每秒更新
        self.show()
        print(f"开始正计时模式，提醒时间点：{self.config_manager.config.get('warning_times', [])}")

    def add_close_button(self):
        """为正计时模式添加关闭按钮"""
        # 检查是否已经有关闭按钮
        if hasattr(self, 'close_button'):
            return
        
        # 增加窗口高度以容纳按钮
        self.setFixedSize(300, 150)
        
        # 创建关闭按钮
        self.close_button = QPushButton("关闭计时")
        self.close_button.clicked.connect(self.close_countup)
        
        # 将按钮添加到布局中
        layout = self.layout()
        if layout:
            layout.addWidget(self.close_button)

    def close_countup(self):
        """关闭正计时"""
        print(f"正计时结束，总计时时间：{self.remaining_time}秒")
        self.timer.stop()
        self.hide()

    def start_countdown_internal(self, duration):
        """启动倒计时模式（内部方法，保持原有功能）"""
        config = self.config_manager.config

        # 移除正计时的关闭按钮（如果存在）
        self.remove_close_button()

        # 检查是否启用分段模式
        if config.get('segment_mode', False) and config.get('segments', []):
            self.is_segment_mode = True
            self.segment_times = config['segments'].copy()
            self.current_segment_index = 0
            self.remaining_time = self.segment_times[0]
            self.total_time = sum(self.segment_times)
            print(f"开始分段计时，共{len(self.segment_times)}段，当前第1段：{self.segment_times[0]}秒")
        else:
            self.is_segment_mode = False
            self.remaining_time = duration
            self.total_time = duration
        
        # 检查是否启用循环模式
        if config.get('loop_mode', False):
            self.is_loop_mode = True
            self.total_loops = config.get('loop_count', 1)
            self.current_loop = 1
            print(f"开始循环计时，共{self.total_loops}轮，当前第1轮")
        else:
            self.is_loop_mode = False
            self.current_loop = 0
            self.total_loops = 0
        
        # 恢复倒计时窗口大小和样式
        self.setFixedSize(300, 120)
        self.setStyleSheet("""
            QWidget {
                background-color: rgba(255, 0, 0, 200);
                border: 2px solid red;
                border-radius: 10px;
            }
            QLabel {
                color: white;
                font-weight: bold;
            }
        """)
        
        self.warned_times.clear()
        self.update_display()
        self.timer.start(1000)  # 每秒更新
        self.show()

    def remove_close_button(self):
        """移除正计时模式的关闭按钮"""
        if hasattr(self, 'close_button'):
            self.close_button.setParent(None)
            delattr(self, 'close_button')
    
    def stop_countdown(self):
        """停止倒计时"""
        self.timer.stop()
        self.shake_timer.stop()
        self.overtime_timer.stop()  # 停止超时计时器
        self.is_overtime_mode = False
        self.hide()
        
        # 保存窗口位置
        if self.config_manager.config['remember_position']:
            pos = self.pos()
            self.config_manager.config['window_x'] = pos.x()
            self.config_manager.config['window_y'] = pos.y()
            self.config_manager.save_config()
    
    def stop_timer_only(self):
        """只停止计时器，不隐藏窗口（用于正计时的PPT退出处理）"""
        self.timer.stop()
        self.shake_timer.stop()
        self.overtime_timer.stop()  # 停止超时计时器
        self.is_overtime_mode = False
        
        # 保存窗口位置
        if self.config_manager.config['remember_position']:
            pos = self.pos()
            self.config_manager.config['window_x'] = pos.x()
            self.config_manager.config['window_y'] = pos.y()
            self.config_manager.save_config()
    
    def update_countdown(self):
        """更新计时器（倒计时或正计时）"""
        if getattr(self, 'is_countup', False):
            # 正计时模式：完全独立的更新逻辑
            self.update_countup()
        else:
            # 倒计时模式：保持原有逻辑
            self.update_countdown_internal()

    def update_countup(self):
        """更新正计时"""
        # 正计时：时间递增
        self.remaining_time += 1
        self.update_display()
        
        # 检查提醒时间点
        config = self.config_manager.config
        if config['sound_enabled'] or config['vibration_enabled']:
            for warning_time in config.get('warning_times', []):
                # 正计时：已经过的时间达到提醒点
                if self.remaining_time == warning_time and warning_time not in self.warned_times:
                    self.warned_times.add(warning_time)
                    self.show_warning()
                    print(f"正计时提醒：已计时{warning_time}秒")
        
        # 正计时没有自动结束，继续运行直到用户手动关闭

    def update_countdown_internal(self):
        """更新倒计时（内部方法，保持原有功能）"""
        if self.is_overtime_mode:
            # 超时模式：保持原有行为（基于倒计时的负数）
            self.remaining_time -= 1
        else:
            # 倒计时递减
            self.remaining_time -= 1
        
        self.update_display()
        
        # 检查警告时间（仅在正常模式下）
        if not self.is_overtime_mode:
            config = self.config_manager.config
            if config['sound_enabled'] or config['vibration_enabled']:
                for warning_time in config.get('warning_times', []):
                    # 倒计时：剩余时间等于提醒点
                    if self.remaining_time == warning_time and warning_time not in self.warned_times:
                        self.warned_times.add(warning_time)
                        self.show_warning()
        
        # 时间到处理（倒计时模式）
        if not self.is_overtime_mode:
            # 倒计时：剩余时间<=0
            if self.remaining_time <= 0:
                if self.is_segment_mode:
                    self.handle_segment_complete()
                elif self.is_loop_mode:
                    self.handle_loop_complete()
                else:
                    self.time_up()
    
    def handle_segment_complete(self):
        """处理分段完成"""
        config = self.config_manager.config
        
        # 分段结束提醒
        print(f"第{self.current_segment_index + 1}段计时完成")
        if config['sound_enabled']:
            try:
                winsound.MessageBeep(winsound.MB_ICONASTERISK)
            except:
                pass
        
        if config['vibration_enabled']:
            self.start_shake()
        
        # 检查是否还有下一段
        self.current_segment_index += 1
        if self.current_segment_index < len(self.segment_times):
            # 开始下一段
            if getattr(self, 'is_countup', False):
                # 正计时：下一段从0开始，total_time为段长度
                self.remaining_time = 0
                self.total_time = self.segment_times[self.current_segment_index]
            else:
                self.remaining_time = self.segment_times[self.current_segment_index]
                self.total_time = sum(self.segment_times)
            self.warned_times.clear()
            print(f"开始第{self.current_segment_index + 1}段计时：{self.remaining_time}秒")
        else:
            # 所有分段完成
            print("所有分段计时完成")
            if self.is_loop_mode:
                self.handle_loop_complete()
            else:
                self.time_up()
    
    def handle_loop_complete(self):
        """处理循环完成"""
        self.current_loop += 1
        
        if self.current_loop <= self.total_loops:
            # 开始下一轮循环
            print(f"开始第{self.current_loop}轮循环")
            if self.is_segment_mode:
                # 重置分段
                self.current_segment_index = 0
                if getattr(self, 'is_countup', False):
                    self.remaining_time = 0
                    self.total_time = self.segment_times[0]
                else:
                    self.remaining_time = self.segment_times[0]
                    self.total_time = sum(self.segment_times)
            else:
                # 重置普通计时
                config = self.config_manager.config
                if getattr(self, 'is_countup', False):
                    self.remaining_time = 0
                    self.total_time = config['timer_duration']
                else:
                    self.remaining_time = config['timer_duration']
                    self.total_time = config['timer_duration']
            
            self.warned_times.clear()
            
            # 循环间隔提醒
            config = self.config_manager.config
            if config['sound_enabled']:
                try:
                    winsound.MessageBeep(winsound.MB_OK)
                except:
                    pass
        else:
            # 所有循环完成
            print("所有循环计时完成")
            self.time_up()
    
    def update_display(self):
        """更新显示（倒计时或正计时）"""
        if getattr(self, 'is_countup', False):
            self.update_countup_display()
        else:
            self.update_countdown_display()

    def update_countup_display(self):
        """更新正计时显示"""
        total_seconds = int(self.remaining_time)
        hours = total_seconds // 3600
        minutes = (total_seconds % 3600) // 60
        seconds = total_seconds % 60

        # 正计时显示格式：已计时时间
        if hours > 0:
            time_text = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
        else:
            time_text = f"{minutes:02d}:{seconds:02d}"
        
        # 添加正计时标识
        time_text = f" {time_text}"

        self.time_label.setText(time_text)

        # 正计时不需要变红逻辑，保持绿色主题
        self.time_label.setStyleSheet("color: #2E7D32; font-size: 48px; font-weight: bold;")

    def update_countdown_display(self):
        """更新倒计时显示（保持原有功能）"""
        total_seconds = abs(int(self.remaining_time))  # 使用绝对值计算时分秒
        hours = total_seconds // 3600
        minutes = (total_seconds % 3600) // 60
        seconds = total_seconds % 60

        # 基础时间显示，自动适配小时
        if hours > 0:
            time_text = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
        else:
            time_text = f"{minutes:02d}:{seconds:02d}"
        
        # 倒计时：若为负数显示超时
        if self.remaining_time < 0:
            time_text = f"-{time_text}\n超时"

        # 添加分段信息
        if self.is_segment_mode:
            time_text += f"\n段{self.current_segment_index + 1}/{len(self.segment_times)}"

        # 添加循环信息
        if self.is_loop_mode:
            time_text += f"\n轮{self.current_loop}/{self.total_loops}"

        self.time_label.setText(time_text)

        # 根据用户设置的提醒时间点变红，取最小值作为变红阈值
        config = self.config_manager.config
        warning_times = config.get('warning_times', [30])
        red_threshold = min(warning_times) if warning_times else 30
        
        # 倒计时：剩余时间<=阈值或负数变红
        if self.remaining_time <= red_threshold or self.remaining_time < 0:
            red = True
        else:
            red = False

        if red:
            self.time_label.setStyleSheet("""
                QLabel {
                    color: white;
                    font-size: 24px;
                    font-weight: bold;
                    background: rgba(200, 0, 0, 180);
                    border-radius: 10px;
                    padding: 10px;
                }
            """)
        else:
            self.time_label.setStyleSheet("""
                QLabel {
                    color: white;
                    font-size: 24px;
                    font-weight: bold;
                    background: rgba(0, 0, 0, 180);
                    border-radius: 10px;
                    padding: 10px;
                }
            """)
    
    def show_warning(self):
        """显示警告"""
        config = self.config_manager.config
        
        # 声音提醒
        if config['sound_enabled']:
            try:
                    winsound.MessageBeep(winsound.MB_ICONHAND)
            except:
                pass
        
        # 震动提醒
        if config['vibration_enabled']:
            self.start_shake()
        
        # 黑屏提醒
        if config.get('black_screen_enabled', True):
            self.start_black_screen()
    
    def start_shake(self):
        """开始震动"""
        if not self.shake_timer.isActive():
            self.original_pos = self.pos()
            self.shake_count = 0
            self.shake_timer.start(50)  # 50ms间隔
    
    def shake_step(self):
        """震动步骤"""
        if self.shake_count < 20:  # 震动1秒
            offset = 5 if self.shake_count % 2 == 0 else -5
            if self.original_pos is not None:
                new_pos = QPoint(self.original_pos.x() + offset, self.original_pos.y())
                self.move(new_pos)
            self.shake_count += 1
        else:
            self.shake_timer.stop()
            if self.original_pos is not None:
                self.move(self.original_pos)
    
    def start_black_screen(self):
        """开始黑屏提醒"""
        try:
            # 创建全屏黑色窗口
            from PyQt5.QtCore import Qt
            self.black_screen = QWidget()
            self.black_screen.setWindowTitle("Time's up - 黑屏提醒")
            self.black_screen.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
            self.black_screen.setStyleSheet("background-color: black;")
            
            # 获取屏幕尺寸并设置全屏
            screen = QApplication.primaryScreen()
            if screen:
                geometry = screen.geometry()
                self.black_screen.setGeometry(geometry)
            else:
                self.black_screen.setGeometry(0, 0, 1920, 1080)  # 备用尺寸
            self.black_screen.showFullScreen()
            
            # 2秒后自动关闭黑屏
            QTimer.singleShot(2000, self.stop_black_screen)
            
            print("黑屏提醒已显示")
        except Exception as e:
            print(f"黑屏提醒失败: {e}")
    
    def stop_black_screen(self):
        """停止黑屏提醒"""
        try:
            if hasattr(self, 'black_screen') and self.black_screen:
                self.black_screen.close()
                self.black_screen = None
                print("黑屏提醒已关闭")
        except Exception as e:
            print(f"关闭黑屏失败: {e}")
    
    def overtime_black_screen(self):
        """超时黑屏提醒"""
        try:
            print("超时黑屏提醒")
            # 创建全屏黑色窗口
            from PyQt5.QtCore import Qt
            self.overtime_black_screen_widget = QWidget()
            self.overtime_black_screen_widget.setWindowTitle("Time's up - 超时提醒")
            self.overtime_black_screen_widget.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
            self.overtime_black_screen_widget.setStyleSheet("background-color: black;")
            
            # 获取屏幕尺寸并设置全屏
            screen = QApplication.primaryScreen()
            if screen:
                geometry = screen.geometry()
                self.overtime_black_screen_widget.setGeometry(geometry)
            else:
                self.overtime_black_screen_widget.setGeometry(0, 0, 1920, 1080)  # 备用尺寸
            self.overtime_black_screen_widget.showFullScreen()
            
            # 1秒后自动关闭黑屏
            QTimer.singleShot(1000, self.stop_overtime_black_screen)
            
        except Exception as e:
            print(f"黑屏提醒失败: {e}")
    
    def stop_overtime_black_screen(self):
        """停止超时黑屏提醒"""
        try:
            if hasattr(self, 'overtime_black_screen_widget') and self.overtime_black_screen_widget:
                self.overtime_black_screen_widget.close()
                self.overtime_black_screen_widget = None
                print("超时黑屏提醒已关闭")
        except Exception as e:
            print(f"关闭超时黑屏失败: {e}")
    
    def time_up(self):
        """时间到处理"""
        config = self.config_manager.config
        
        # 检查是否启用超时模式
        if config['exit_mode'] == 'overtime_mode':
            print("进入超时模式，开始负时间计时")
            self.is_overtime_mode = True
            
            # 启动超时黑屏提醒计时器
            interval = config.get('overtime_black_screen_interval', 30) * 1000  # 转换为毫秒
            self.overtime_timer.start(interval)
            
            # 立即显示一次黑屏提醒
            self.overtime_black_screen()
            
            # 继续计时（负时间）- 不停止主计时器
            return
        
        # 原有的退出逻辑
        self.timer.stop()
        
        # 最终警告
        if config['sound_enabled']:
            try:
                    winsound.MessageBeep(winsound.MB_ICONEXCLAMATION)
            except:
                pass
        
        if config['vibration_enabled']:
            self.start_shake()
        
        # 执行退出操作
        self.execute_exit_action()
        
        # 隐藏窗口
        QTimer.singleShot(2000, self.hide)
    
    def execute_exit_action(self):
        """执行退出操作"""
        config = self.config_manager.config
        if config['exit_mode'] == 'exit_fullscreen':
            # 发送ESC键退出全屏
            try:
                print("发送ESC键退出全屏...")
                
                # 方法1：先找到PPT全屏窗口并激活
                ppt_hwnd = None
                try:
                    def find_ppt_window(hwnd, windows):
                        if win32gui.IsWindowVisible(hwnd):
                            try:
                                title = win32gui.GetWindowText(hwnd).lower()
                                if any(keyword in title for keyword in [
                                    '幻灯片放映', 'slide show', 'slideshow', '演示播放'
                                ]):
                                    windows.append(hwnd)
                            except:
                                pass
                        return True
                    
                    windows = []
                    win32gui.EnumWindows(find_ppt_window, windows)
                    if windows:
                        ppt_hwnd = windows[0]
                        print(f"找到PPT窗口: {hex(ppt_hwnd)}")
                        
                        # 确保PPT窗口在前台
                        win32gui.SetForegroundWindow(ppt_hwnd)
                        time.sleep(0.2)  # 等待窗口激活
                except Exception as e:
                    print(f"查找PPT窗口失败: {e}")
                
                # 方法2：使用win32api发送ESC键
                try:
                    win32api.keybd_event(0x1B, 0, 0, 0)  # ESC键按下
                    time.sleep(0.1)
                    win32api.keybd_event(0x1B, 0, win32con.KEYEVENTF_KEYUP, 0)  # ESC键释放
                    print("✓ 方法1: win32api ESC键已发送")
                except Exception as e1:
                    print(f"方法1失败: {e1}")
                    
                    # 方法3：使用ctypes发送ESC键
                    try:
                        import ctypes
                        VK_ESCAPE = 0x1B
                        KEYEVENTF_KEYUP = 0x0002
                        user32 = ctypes.windll.user32
                        
                        user32.keybd_event(VK_ESCAPE, 0, 0, 0)  # 按下ESC
                        time.sleep(0.1)
                        user32.keybd_event(VK_ESCAPE, 0, KEYEVENTF_KEYUP, 0)  # 释放ESC
                        print("✓ 方法2: ctypes ESC键已发送")
                    except Exception as e2:
                        print(f"方法2失败: {e2}")
                        
                        # 方法4：如果有PPT窗口句柄，直接发送消息
                        if ppt_hwnd:
                            try:
                                win32gui.PostMessage(ppt_hwnd, win32con.WM_KEYDOWN, 0x1B, 0)
                                time.sleep(0.05)
                                win32gui.PostMessage(ppt_hwnd, win32con.WM_KEYUP, 0x1B, 0)
                                print("✓ 方法3: PostMessage ESC键已发送")
                            except Exception as e3:
                                print(f"方法3失败: {e3}")
                
            except Exception as e:
                print(f"发送ESC键失败: {e}")
                
        elif config['exit_mode'] == 'close_ppt':
            # 关闭PPT进程
            try:
                print("尝试关闭PPT进程...")
                closed_any = False
                
                for proc in psutil.process_iter(['pid', 'name']):
                    try:
                        proc_name = proc.info['name']
                        if proc_name in ['POWERPNT.EXE', 'wps.exe', 'wpp.exe']:
                            print(f"关闭进程: {proc_name} (PID: {proc.info['pid']})")
                            proc.terminate()
                            closed_any = True
                            
                            # 等待进程结束，如果不行就强制杀死
                            try:
                                proc.wait(timeout=3)
                            except psutil.TimeoutExpired:
                                proc.kill()
                                print(f"强制杀死进程: {proc_name}")
                                
                    except (psutil.NoSuchProcess, psutil.AccessDenied):
                        continue
                
                if closed_any:
                    print("✓ PPT进程已关闭")
                else:
                    print("○ 未找到运行中的PPT进程")
                    
            except Exception as e:
                print(f"关闭PPT失败: {e}")


class SettingsDialog(QDialog):
    """设置对话框"""
    
    def __init__(self, config_manager):
        super().__init__()
        self.config_manager = config_manager
        self.init_ui()
    
    def init_ui(self):
        """初始化UI"""
        self.setWindowTitle("Time's up - 设置")
        self.setWindowIcon(SystemTrayIcon.get_clock_icon())
        self.setModal(True)
        self.resize(400, 500)

        layout = QVBoxLayout()

        # 基础设定
        basic_group = QGroupBox("基础设定")
        basic_layout = QFormLayout()

        # 倒计时时长（小时:分钟:秒格式输入）
        duration_layout = QHBoxLayout()
        self.hour_spin = QSpinBox()
        self.hour_spin.setRange(0, 23)
        self.hour_spin.setSuffix(" 时")
        self.minute_spin = QSpinBox()
        self.minute_spin.setRange(0, 59)
        self.minute_spin.setSuffix(" 分")
        self.second_spin = QSpinBox()
        self.second_spin.setRange(0, 59)
        self.second_spin.setSuffix(" 秒")
        # 初始化值
        total_seconds = self.config_manager.config['timer_duration']
        self.hour_spin.setValue(total_seconds // 3600)
        self.minute_spin.setValue((total_seconds % 3600) // 60)
        self.second_spin.setValue(total_seconds % 60)
        duration_layout.addWidget(self.hour_spin)
        duration_layout.addWidget(QLabel(":"))
        duration_layout.addWidget(self.minute_spin)
        duration_layout.addWidget(QLabel(":"))
        duration_layout.addWidget(self.second_spin)
        basic_layout.addRow("倒计时时长:", duration_layout)

        # 计时模式：倒计时 / 正计时
        self.mode_combo = QComboBox()
        self.mode_combo.addItems(["倒计时", "正计时"])
        # 根据配置设置默认值（兼容旧配置）
        cfg_mode = self.config_manager.config.get('timer_mode', 'countdown')
        if cfg_mode == 'countup':
            self.mode_combo.setCurrentIndex(1)
        else:
            self.mode_combo.setCurrentIndex(0)
        basic_layout.addRow("计时模式:", self.mode_combo)

        # 退出方式
        self.exit_mode_combo = QComboBox()
        self.exit_mode_combo.addItems(["退出全屏", "关闭PPT", "超时模式"])
        if self.config_manager.config['exit_mode'] == 'close_ppt':
            self.exit_mode_combo.setCurrentIndex(1)
        elif self.config_manager.config['exit_mode'] == 'overtime_mode':
            self.exit_mode_combo.setCurrentIndex(2)
        basic_layout.addRow("时间到后:", self.exit_mode_combo)

        # 记住位置
        self.remember_pos_check = QCheckBox()
        self.remember_pos_check.setChecked(self.config_manager.config['remember_position'])
        basic_layout.addRow("记住窗口位置:", self.remember_pos_check)

        # 声音提醒
        self.sound_check = QCheckBox()
        self.sound_check.setChecked(self.config_manager.config['sound_enabled'])
        basic_layout.addRow("声音提醒:", self.sound_check)

        # 震动提醒
        self.vibration_check = QCheckBox()
        self.vibration_check.setChecked(self.config_manager.config['vibration_enabled'])
        basic_layout.addRow("震动提醒:", self.vibration_check)

        # 黑屏提醒
        self.black_screen_check = QCheckBox()
        self.black_screen_check.setChecked(self.config_manager.config.get('black_screen_enabled', True))
        basic_layout.addRow("黑屏提醒:", self.black_screen_check)

        basic_group.setLayout(basic_layout)
        layout.addWidget(basic_group)

        # 高级设定按钮
        advanced_btn = QPushButton("高级设定")
        advanced_btn.clicked.connect(self.show_advanced_settings)
        layout.addWidget(advanced_btn)

        # 按钮
        button_layout = QHBoxLayout()
        ok_btn = QPushButton("确定")
        cancel_btn = QPushButton("取消")
        ok_btn.clicked.connect(self.accept)
        cancel_btn.clicked.connect(self.reject)
        button_layout.addWidget(ok_btn)
        button_layout.addWidget(cancel_btn)

        layout.addLayout(button_layout)
        self.setLayout(layout)
    
    def show_advanced_settings(self):
        """显示高级设定"""
        dialog = AdvancedSettingsDialog(self.config_manager, self)
        dialog.exec_()
    
    def accept(self):
        """确定按钮"""
        config = self.config_manager.config
        # 获取小时:分钟:秒并转换为总秒数
        hours = self.hour_spin.value()
        minutes = self.minute_spin.value()
        seconds = self.second_spin.value()
        total_seconds = hours * 3600 + minutes * 60 + seconds
        # 限制最小10秒，最大7200秒
        if total_seconds < 10:
            QMessageBox.warning(self, "错误", "倒计时时长不能少于10秒！")
            return
        if total_seconds > 7200:
            QMessageBox.warning(self, "错误", "倒计时时长不能超过2小时！")
            return
        config['timer_duration'] = total_seconds
        # 保存计时模式
        if hasattr(self, 'mode_combo') and self.mode_combo.currentIndex() == 1:
            config['timer_mode'] = 'countup'
        else:
            config['timer_mode'] = 'countdown'
        if self.exit_mode_combo.currentIndex() == 1:
            config['exit_mode'] = 'close_ppt'
        elif self.exit_mode_combo.currentIndex() == 2:
            config['exit_mode'] = 'overtime_mode'
        else:
            config['exit_mode'] = 'exit_fullscreen'
        config['remember_position'] = self.remember_pos_check.isChecked()
        config['sound_enabled'] = self.sound_check.isChecked()
        config['vibration_enabled'] = self.vibration_check.isChecked()
        config['black_screen_enabled'] = self.black_screen_check.isChecked()

        self.config_manager.save_config()
        super().accept()


class AdvancedSettingsDialog(QDialog):
    """高级设定对话框"""
    
    def __init__(self, config_manager, parent_dialog=None):
        super().__init__()
        self.config_manager = config_manager
        self.parent_dialog = parent_dialog  # 主设置对话框的引用
        self.init_ui()
    
    def init_ui(self):
        """初始化UI"""
        self.setWindowTitle("Time's up - 高级设定")
        # 设置窗口图标为小钟表
        self.setWindowIcon(SystemTrayIcon.get_clock_icon())
        self.setModal(True)
        self.resize(450, 400)
        
        layout = QVBoxLayout()
        
        # 提醒时间点
        warning_group = QGroupBox("提醒时间点")
        warning_layout = QVBoxLayout()
        
        self.warning_list = QListWidget()
        for time_point in self.config_manager.config['warning_times']:
            self.warning_list.addItem(f"{time_point} 秒")
        
        warning_controls = QHBoxLayout()
        add_warning_btn = QPushButton("添加")
        remove_warning_btn = QPushButton("删除")
        add_warning_btn.clicked.connect(self.add_warning_time)
        remove_warning_btn.clicked.connect(self.remove_warning_time)
        warning_controls.addWidget(add_warning_btn)
        warning_controls.addWidget(remove_warning_btn)
        
        warning_layout.addWidget(self.warning_list)
        warning_layout.addLayout(warning_controls)
        warning_group.setLayout(warning_layout)
        layout.addWidget(warning_group)
        
        # 分段计时
        segment_group = QGroupBox("分段计时")
        segment_layout = QVBoxLayout()
        
        self.segment_check = QCheckBox("启用分段计时")
        self.segment_check.setChecked(self.config_manager.config.get('segment_mode', False))
        self.segment_check.toggled.connect(self.on_segment_mode_changed)  # 添加信号连接
        segment_layout.addWidget(self.segment_check)
        
        # 分段列表
        self.segment_list = QListWidget()
        for segment_time in self.config_manager.config.get('segments', [300, 300, 300]):
            self.segment_list.addItem(f"{segment_time} 秒")
        
        segment_controls = QHBoxLayout()
        add_segment_btn = QPushButton("添加分段")
        remove_segment_btn = QPushButton("删除分段")
        add_segment_btn.clicked.connect(self.add_segment_time)
        remove_segment_btn.clicked.connect(self.remove_segment_time)
        segment_controls.addWidget(add_segment_btn)
        segment_controls.addWidget(remove_segment_btn)
        
        segment_layout.addWidget(self.segment_list)
        segment_layout.addLayout(segment_controls)
        segment_group.setLayout(segment_layout)
        layout.addWidget(segment_group)
        
        # 循环计时
        loop_group = QGroupBox("循环计时")
        loop_layout = QVBoxLayout()
        
        self.loop_check = QCheckBox("启用循环计时")
        self.loop_check.setChecked(self.config_manager.config.get('loop_mode', False))
        loop_layout.addWidget(self.loop_check)
        
        # 循环次数设置
        loop_count_layout = QHBoxLayout()
        loop_count_layout.addWidget(QLabel("循环次数:"))
        self.loop_count_spin = QSpinBox()
        self.loop_count_spin.setRange(1, 100)
        self.loop_count_spin.setValue(self.config_manager.config.get('loop_count', 3))
        loop_count_layout.addWidget(self.loop_count_spin)
        loop_layout.addLayout(loop_count_layout)
        
        loop_group.setLayout(loop_layout)
        layout.addWidget(loop_group)
        
        # 超时模式设置
        overtime_group = QGroupBox("超时模式设置")
        overtime_layout = QVBoxLayout()
        
        # 黑屏间隔设置
        interval_layout = QHBoxLayout()
        interval_layout.addWidget(QLabel("黑屏提醒间隔:"))
        self.interval_spin = QSpinBox()
        self.interval_spin.setRange(10, 300)  # 10秒到5分钟
        self.interval_spin.setSuffix(" 秒")
        self.interval_spin.setValue(self.config_manager.config.get('overtime_black_screen_interval', 30))
        interval_layout.addWidget(self.interval_spin)
        overtime_layout.addLayout(interval_layout)
        
        overtime_group.setLayout(overtime_layout)
        layout.addWidget(overtime_group)
        
        # 按钮
        button_layout = QHBoxLayout()
        ok_btn = QPushButton("确定")
        cancel_btn = QPushButton("取消")
        ok_btn.clicked.connect(self.accept)
        cancel_btn.clicked.connect(self.reject)
        button_layout.addWidget(ok_btn)
        button_layout.addWidget(cancel_btn)
        
        layout.addLayout(button_layout)
        self.setLayout(layout)
    
    def prompt_for_duration_seconds(self, title="请输入时间"):
        """通用小时:分钟:秒输入对话框，返回总秒数或 None"""
        dlg = QDialog(self)
        dlg.setWindowTitle(title)
        dlg.setModal(True)

        vbox = QVBoxLayout()
        hbox = QHBoxLayout()

        hour_spin = QSpinBox()
        hour_spin.setRange(0, 23)
        hour_spin.setSuffix(" 时")
        minute_spin = QSpinBox()
        minute_spin.setRange(0, 59)
        minute_spin.setSuffix(" 分")
        second_spin = QSpinBox()
        second_spin.setRange(0, 59)
        second_spin.setSuffix(" 秒")

        hbox.addWidget(hour_spin)
        hbox.addWidget(QLabel(":"))
        hbox.addWidget(minute_spin)
        hbox.addWidget(QLabel(":"))
        hbox.addWidget(second_spin)

        vbox.addLayout(hbox)

        btn_layout = QHBoxLayout()
        ok_btn = QPushButton("确定")
        cancel_btn = QPushButton("取消")
        ok_btn.clicked.connect(dlg.accept)
        cancel_btn.clicked.connect(dlg.reject)
        btn_layout.addWidget(ok_btn)
        btn_layout.addWidget(cancel_btn)

        vbox.addLayout(btn_layout)
        dlg.setLayout(vbox)

        if dlg.exec_() == QDialog.Accepted:
            total = hour_spin.value() * 3600 + minute_spin.value() * 60 + second_spin.value()
            if total <= 0:
                QMessageBox.warning(self, "错误", "请输入大于0的时间（至少1秒）")
                return None
            return total
        return None
    
    def add_segment_time(self):
        """添加分段时间"""
        seconds = self.prompt_for_duration_seconds("添加分段时间")
        if seconds is not None:
            self.segment_list.addItem(f"{seconds} 秒")
    
    def remove_segment_time(self):
        """删除分段时间"""
        current_row = self.segment_list.currentRow()
        if current_row >= 0:
            self.segment_list.takeItem(current_row)
    
    def add_warning_time(self):
        """添加提醒时间"""
        seconds = self.prompt_for_duration_seconds("添加提醒时间")
        if seconds is not None:
            self.warning_list.addItem(f"{seconds} 秒")
    
    def remove_warning_time(self):
        """删除提醒时间"""
        current_row = self.warning_list.currentRow()
        if current_row >= 0:
            self.warning_list.takeItem(current_row)
    
    def accept(self):
        """确定按钮"""
        # 保存提醒时间点
        warning_times = []
        for i in range(self.warning_list.count()):
            item = self.warning_list.item(i)
            if item:
                item_text = item.text()
                time_val = int(item_text.split()[0])
                warning_times.append(time_val)
        
        # 保存分段时间
        segments = []
        for i in range(self.segment_list.count()):
            item = self.segment_list.item(i)
            if item:
                item_text = item.text()
                time_val = int(item_text.split()[0])
                segments.append(time_val)
        
        # 更新配置
        config = self.config_manager.config
        config['warning_times'] = sorted(warning_times, reverse=True)
        config['segment_mode'] = self.segment_check.isChecked()
        config['segments'] = segments if segments else [300, 300, 300]  # 默认值
        config['loop_mode'] = self.loop_check.isChecked()
        config['loop_count'] = self.loop_count_spin.value()
        config['overtime_black_screen_interval'] = self.interval_spin.value()
        
        self.config_manager.save_config()
        super().accept()
    
    def on_segment_mode_changed(self, checked):
        """当分段模式改变时调用"""
        if checked and self.parent_dialog:
            # 如果启用分段计时，自动取消黑屏提醒（除非用户后续手动勾选）
            if hasattr(self.parent_dialog, 'black_screen_check'):
                self.parent_dialog.black_screen_check.setChecked(False)
                print("已启用分段计时，自动取消黑屏提醒")  # 调试信息


class SystemTrayIcon(QSystemTrayIcon):
    """系统托盘图标"""
    
    def __init__(self, app):
        super().__init__()
        self.app = app
        self.config_manager = ConfigManager()
        self.ppt_monitor = PPTMonitor()
        self.countdown_window = CountdownWindow(self.config_manager)
        
        self.init_ui()
        self.setup_connections()
        
        # 启动PPT监控
        self.ppt_monitor.start()
    
    @staticmethod
    def get_clock_icon():
        """静态方法：创建钟表图标，供其他窗口复用"""
        pixmap = QPixmap(32, 32)
        pixmap.fill(QColor(0, 0, 0, 0))
        
        painter = QPainter(pixmap)
        painter.setRenderHint(QPainter.Antialiasing)
        
        # 绘制时钟图标
        painter.setBrush(QBrush(QColor(70, 130, 180)))
        painter.setPen(QPen(QColor(255, 255, 255), 2))
        painter.drawEllipse(2, 2, 28, 28)
        
        # 绘制时针
        painter.setPen(QPen(QColor(255, 255, 255), 3))
        painter.drawLine(16, 16, 16, 8)
        painter.drawLine(16, 16, 22, 16)
        
        painter.end()
        
        return QIcon(pixmap)
    
    def init_ui(self):
        """初始化UI"""
        # 创建图标
        icon = self.create_icon()
        self.setIcon(icon)
        self.setToolTip("Time's up - PPT自动计时工具")
        
        # 创建菜单
        menu = QMenu()
        
        # 手动启动计时
        start_action = QAction("手动启动计时", self)
        start_action.triggered.connect(self.manual_start)
        menu.addAction(start_action)
        
        # 停止计时
        stop_action = QAction("停止计时", self)
        stop_action.triggered.connect(self.stop_timer)
        menu.addAction(stop_action)
        
        menu.addSeparator()
        
        # 设置
        settings_action = QAction("设置", self)
        settings_action.triggered.connect(self.show_settings)
        menu.addAction(settings_action)
        
        # 关于
        about_action = QAction("关于", self)
        about_action.triggered.connect(self.show_about)
        menu.addAction(about_action)
        
        menu.addSeparator()
        
        # 退出
        quit_action = QAction("退出", self)
        quit_action.triggered.connect(self.quit_app)
        menu.addAction(quit_action)
        
        self.setContextMenu(menu)
    
    def create_icon(self):
        """创建托盘图标"""
        # 复用静态方法
        return self.get_clock_icon()
    
    def setup_connections(self):
        """设置信号连接"""
        self.ppt_monitor.ppt_detected.connect(self.on_ppt_detected)
        self.ppt_monitor.ppt_lost.connect(self.on_ppt_lost)
        self.activated.connect(self.on_tray_activated)
    
    def on_tray_activated(self, reason):
        """托盘图标激活"""
        if reason == QSystemTrayIcon.ActivationReason.DoubleClick:
            self.manual_start()
    
    def on_ppt_detected(self, ppt_type):
        """检测到PPT全屏"""
        # 移除提示信息，直接开始计时
        duration = self.config_manager.config['timer_duration']
        self.countdown_window.start_countdown(duration)
    
    def on_ppt_lost(self):
        """PPT全屏丢失"""
        # 根据计时模式选择不同的退出策略
        if getattr(self.countdown_window, 'is_countup', False):
            # 正计时模式：只停止计时，不隐藏窗口，让用户手动关闭
            self.countdown_window.stop_timer_only()
        else:
            # 倒计时模式：完全停止并隐藏窗口
            self.countdown_window.stop_countdown()
    
    def manual_start(self):
        """手动启动计时"""
        duration = self.config_manager.config['timer_duration']
        self.countdown_window.start_countdown(duration)
        # 移除提示信息
    
    def stop_timer(self):
        """停止计时"""
        self.countdown_window.stop_countdown()
        # 移除提示信息
    
    def show_settings(self):
        """显示设置"""
        dialog = SettingsDialog(self.config_manager)
        dialog.exec_()
    
    def show_about(self):
        """显示关于"""
        about = QMessageBox()
        about.setWindowTitle("关于 Time's up")
        # 使用复用的钟表图标
        icon = SystemTrayIcon.get_clock_icon()
        try:
            # 设置窗口图标和消息框左侧图标
            about.setWindowIcon(icon)
        except Exception:
            pass

        about.setText(
            "主要功能：\n"
            "🔍 自动监控: 自动检测PPT/WPS进程，无需手动启动\n"
            "⏰ 智能计时: PPT播放时自动开始倒计时\n"
            "🪟 悬浮窗口: 倒计时窗口可拖动，记忆位置\n"
            "🔔 多重提醒: 声音提醒、闪烁提醒、黑屏提醒，可自定义提醒时间点\n"
            "⚙️ 灵活设置: 丰富的自定义选项\n"
            "🎯 智能退出: 时间到后可选择退出全屏、关闭PPT或超时模式\n"
            "⏳ 超时模式: 计时器变为负时间，定期黑屏提醒\n"
            "⏲ 正计时模式：可进行正计时，便于PPT排练\n\n"
            f"版本:{VERSION}\n"
            "作者: D&Ai"
        )
        
        # 添加检查更新按钮
        about.setStandardButtons(QMessageBox.Ok)
        update_btn = about.addButton("检查更新", QMessageBox.ActionRole)
        
        # 执行对话框
        result = about.exec_()
        
        # 检查用户点击的是哪个按钮
        if about.clickedButton() == update_btn:
            self.check_update()
    
    def check_update(self):
        """检查软件更新"""
        url = "https://gitee.com/daiyixr/times_up/raw/master/latest_versions.json"
        try:
            with urllib.request.urlopen(url, timeout=10) as resp:
                text = resp.read().decode('utf-8')
                info = json.loads(text)
            latest = info.get("version", "")
            if latest and latest != VERSION:
                # 检测到新版本，询问是否下载
                msg = f"当前版本：{VERSION}\n最新版本：{latest}\n\n更新内容：{info.get('notes', '')}\n\n是否打开下载页面？\n"
                if QMessageBox.question(None, "发现新版本", msg, QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
                    # 确认后打开百度网盘下载页面
                    baidu_url = "https://pan.baidu.com/s/1P2Eo2psAwg4B-V9MCi2Cjw?pwd=xgzr"  # 替换为你的真实链接
                    webbrowser.open(baidu_url)
            else:
                QMessageBox.information(None, "检查更新", "当前已是最新版本！")
        except Exception as e:
            QMessageBox.warning(None, "检查更新失败", f"检查更新时发生错误：{str(e)}")
    
    def quit_app(self):
        """退出应用"""
        self.ppt_monitor.stop()
        self.ppt_monitor.wait()
        self.countdown_window.close()
        self.app.quit()


def main():
    """主函数"""
    # 使用互斥锁确保单实例运行
    check_single_instance()
    
    app = QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(False)
    
    # 检查是否支持系统托盘
    if not QSystemTrayIcon.isSystemTrayAvailable():
        QMessageBox.critical(None, "错误", "系统托盘不可用")
        sys.exit(1)
    
    # 创建托盘图标
    tray_icon = SystemTrayIcon(app)
    tray_icon.show()
    
    # 显示启动消息
    tray_icon.showMessage(
        "Time's up",
        "PPT自动计时工具已启动\n双击图标可手动启动计时\n右键图标进入设置界面",
        QSystemTrayIcon.MessageIcon.Information,
        3000
    )
    
    sys.exit(app.exec_())


def check_single_instance():
    """使用系统互斥锁确保单实例运行"""
    try:
        import ctypes
        mutex_name = "times_up_single_instance_mutex"
        kernel32 = ctypes.windll.kernel32
        mutex = kernel32.CreateMutexW(None, False, mutex_name)
        last_error = kernel32.GetLastError()
        # ERROR_ALREADY_EXISTS = 183
        if last_error == 183:
            # 已有实例在运行
            app = QApplication(sys.argv)
            msg_box = QMessageBox()
            msg_box.setWindowTitle("Time's up - 提示")
            msg_box.setIcon(QMessageBox.Warning)
            msg_box.setText("检测到 Time's up 程序已在运行！")
            msg_box.setInformativeText("为避免冲突，请关闭已运行的程序后再启动，或直接使用系统托盘中的程序。")
            msg_box.setStandardButtons(QMessageBox.Ok)
            msg_box.exec_()
            sys.exit(0)
        # 否则正常运行，mutex会在程序退出时自动释放
    except Exception as e:
        # 如果互斥锁创建失败，回退到psutil检测
        print(f"警告: 互斥锁创建失败，回退到进程检测: {e}")
        check_existing_instance()


def check_existing_instance():
    """模糊检测是否已有本程序的实例在运行"""
    try:
        import os
        current_pid = os.getpid()
        # 定义关键字进行模糊匹配
        keywords = ['times_up', 'times up']
        count = 0
        for proc in psutil.process_iter(['pid', 'name', 'exe', 'cmdline']):
            try:
                if proc.info['pid'] == current_pid:
                    continue
                # 获取进程名、exe名、命令行参数
                proc_name = (proc.info.get('name') or '').lower()
                proc_exe = (os.path.basename(proc.info.get('exe') or '')).lower()
                cmdline = ' '.join(str(item).lower() for item in proc.info.get('cmdline', []) if item)
                # 模糊匹配：只要包含关键字就算已运行
                if any(kw in proc_name or kw in proc_exe or kw in cmdline for kw in keywords):
                    count += 1
            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                continue
        if count > 0:
            temp_app = QApplication(sys.argv)
            msg_box = QMessageBox()
            msg_box.setWindowTitle("Time's up - 提示")
            msg_box.setIcon(QMessageBox.Warning)
            msg_box.setText("检测到 Time's up 程序已在运行！")
            msg_box.setInformativeText("为避免冲突，请关闭已运行的程序后再启动，或直接使用系统托盘中的程序。")
            msg_box.setStandardButtons(QMessageBox.Ok)
            msg_box.exec_()
            sys.exit(0)
    except ImportError:
        print("警告: psutil模块不可用，跳过进程重复检测。建议运行: pip install psutil")
        return
    except Exception as e:
        print(f"警告: 进程检测时发生错误: {e}")
        return


if __name__ == "__main__":
    main()